package model.optimization;

import model.ModelPart;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;


import common.notification.EmailNotifier;
import model.AbstractModel;

public class ModelOptimization<T extends AbstractModel> {

	final T model;
 
	List<ModelPart> modelParts = new ArrayList<ModelPart>();
	List<PartOptimizer> optimizers = new ArrayList<PartOptimizer>();
	Class<? extends PartOptimizer> method;

	
	public ModelOptimization(T model, Class<? extends PartOptimizer> method) {
		this.model = model; 
		this.method = method;
	}
	
	public void addModelPart(ModelPart part) throws InstantiationException, IllegalAccessException
	{
		modelParts.add(part);
		PartOptimizer po = method.newInstance();
		po.set(po.getName()+"-"+part.getName(), part);
		optimizers.add(po);
	}
	
	
	public void searchParameters() throws IOException
	{
		Random rnd = new Random(System.currentTimeMillis());
		
		for (PartOptimizer po : optimizers) po.init();
		while(true) {
			
			for (PartOptimizer po : optimizers) po.setParameters();

			// run model on each core
			List<Future<File>> futures = new ArrayList<Future<File>>();
			for (int core = 0; core < Runtime.getRuntime().availableProcessors(); core++) {
				String id = "ModelOptimization-" + core;
				model.initRun(id, Math.abs(rnd.nextLong()));
				futures.add(model.run());
			}

			// Parse results
			model.clearResults();
			for (Future<File> f : futures) {
				try {
					model.addResult(f.get());
				} catch (InterruptedException ex) {
					Logger.getLogger(ModelOptimization.class.getName()).log(Level.SEVERE, null, ex);
				} catch (ExecutionException ex) {
					Logger.getLogger(ModelOptimization.class.getName()).log(Level.SEVERE, null, ex);
				}
			}
			model.parseResults();

			
			// Notify
			for (PartOptimizer po : optimizers) po.modelComputed(model);
			
			boolean terminate = true;
			for (PartOptimizer po : optimizers) if (!po.isDone()) terminate = false;
			if (terminate) break;
		}
		
		EmailNotifier.notifyNoFail("Stop");
	}
}
